package com.tsfyun.common.base.validator;

import com.google.common.collect.Maps;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.util.VoidFunc;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import javax.validation.ConstraintViolation;
import javax.validation.Path;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * hibernate-validator校验工具类
 * <p>
 * 参考文档：http://docs.jboss.org/hibernate/validator/5.4/reference/en-US/html_single/
 *
 *
 */
public class ValidatorUtils {
    private static Validator validator;

    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param rowNo  行号，便于导入数据集合提示第几行
     * @param groups 待校验的组
     * @throws ServiceException 校验不通过，则报ServiceException异常
     */
    public static void validateEntity(Object object, Integer rowNo, Class<?>... groups)
            throws ServiceException {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            List<ConstraintViolation<Object>> sortedConstraintViolations = new ArrayList<>(constraintViolations);
            sortedConstraintViolations = sortedConstraintViolations.stream().sorted(Comparator.comparing(ConstraintViolation::getMessage)).collect(Collectors.toList());
            ConstraintViolation<Object> constraint = sortedConstraintViolations.iterator().next();
            if (Objects.isNull(rowNo)) {
                throw new ServiceException(constraint.getMessage());
            } else {
                throw new ServiceException(String.format("第%d行%s", rowNo, constraint.getMessage()));
            }
        }
    }

    /**
     * 校验对象（自动解析异常行号）
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws ServiceException 校验不通过，则报ServiceException异常
     */
    public static void validateEntityWithRow(Object object, Class<?>... groups)
            throws ServiceException {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        //转换成有序集合
        if (!constraintViolations.isEmpty()) {
            List<ConstraintViolation<Object>> sortedConstraintViolations = new ArrayList<>(constraintViolations);
            sortedConstraintViolations = sortedConstraintViolations.stream().sorted(Comparator.comparing(ConstraintViolation::getMessage)).collect(Collectors.toList());
            for (ConstraintViolation c : sortedConstraintViolations) {
                Path path = c.getPropertyPath();
                if(Objects.nonNull(path)) {
                    String pathStr = path.toString();
                    //集合
                    if(pathStr.contains("[") && pathStr.contains("]")) {
                        final Integer idx = Integer.parseInt(pathStr.substring(pathStr.lastIndexOf("[") + 1,pathStr.lastIndexOf("]")));
                        throw new ServiceException(String.format("第%d行%s", (idx + 1), c.getMessage()));
                    } else {
                        throw new ServiceException(c.getMessage());
                    }
                }
            }
        }
    }

    /**
     *
     * @return
     */
    public static ObjectError getPermanentError(List<ObjectError> allErrors) {
        //此处按字段排序，以免每次报出来的错误不一致
        ObjectError objectError = allErrors.get(0);
        long fieldErrorCnt = allErrors.stream().filter(r->r instanceof FieldError).count();
        if(allErrors.size() == fieldErrorCnt) {
            Map<String,ObjectError> fieldErrorMap = Maps.newLinkedHashMap();
            allErrors.stream().forEach(objError -> {
                if(objError instanceof FieldError) {
                    fieldErrorMap.put(((FieldError) objError).getField(),objError);
                }
            });
            Collection<String> fieldKeySet = fieldErrorMap.keySet();
            List<String> fieldKeys = new ArrayList<>(fieldKeySet);
            Collections.sort(fieldKeys);

            objectError = fieldErrorMap.get(fieldKeys.get(0));
        }
        return objectError;
    }

    /**
     * 是否正确
     * @param expression
     * @param exceptionSupplier
     * @param <X>
     */
    public static<X extends Throwable> void isTrue(boolean expression, Supplier<? extends X> exceptionSupplier) throws X {
        if(!expression) {
            throw exceptionSupplier.get();
        }
    }

    /**
     * 如果条件成立则执行方法（如果是需要检测然后抛异常请勿调用该方法，请调用isTrue方法）
     * @param expression
     * @param function
     */
    public static void isTrueCall(boolean expression, VoidFunc function) {
        if(expression) {
            function.callWithRuntimeException();
        }
    }

}
